package cn.ratel.aop;

import cn.ratel.component.Enhancers;
import cn.ratel.kit.CollectionKit;
import net.sf.cglib.proxy.MethodProxy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

public class MethodInvocation implements Invocation {
	private static Logger log = LoggerFactory.getLogger(MethodInvocation.class);
	private int index;
	private Object target;
	private Method method;
	private Object[] args;
	private Object returnValue;
	private MethodProxy methodProxy;

	public MethodInvocation(Object target, Method method, Object[] args, MethodProxy methodProxy) {
		this.target = target;
		this.method = method;
		this.args = args;
		this.methodProxy = methodProxy;
	}

	@Override
	public void invoke() {
		List<? extends Enhancers.Enhancer> enhancers = ProxyKit.getEnhancers(method);
		if (CollectionKit.isNotEmpty(enhancers) && index < enhancers.size()) {
			Enhancers.Enhancer enhancer = enhancers.get(index);
			index++;
			enhancer.enhance(this);
		} else {
			try {
				log.debug("========================================================================================");
				log.debug("====[ target  ] = {}", method.toString());
				log.debug("====[ params  ] = {}", Arrays.toString(args));
				log.debug("====[enhancers] = {}", enhancers);
				log.debug("========================================================================================");
				returnValue = methodProxy.invokeSuper(target, args);
			} catch (Throwable throwable) {
				throw new RuntimeException("invoke method error: method=" + method.getName(), throwable);
			}
		}
	}

	@Override
	public boolean isController() {
		return this.methodProxy == null;
	}

	@Override
	public Object getReturnValue() {
		return returnValue;
	}

	@Override
	public Class<?> getReturnType() {
		return method.getReturnType();
	}

	@Override
	public void setReturnValue(Object returnValue) {
		this.returnValue = returnValue;
	}

	@Override
	public Object[] getArgs() {
		return args;
	}

	@Override
	public void setArgs(Object[] args) {
		this.args = args;
	}

	@Override
	public Object getTarget() {
		return target;
	}

	@Override
	public Class<?> getRealClass() {
		return target.getClass().getSuperclass();
	}

	@Override
	public Method getMethod() {
		return method;
	}
}
